# -*- coding: utf-8 -*-
"""
音视频同步验证器
用于检测和分析音视频同步问题
"""

import json
import logging
from pathlib import Path
from typing import Dict, Any, List, Optional, Tuple
import subprocess
import tempfile

from .subtitle_parser import SubtitleParser

logger = logging.getLogger(__name__)


class SyncIssue:
    """同步问题类"""

    def __init__(
        self,
        issue_type: str,
        severity: str,
        timestamp: float,
        message: str,
        details: Optional[Dict[str, Any]] = None,
    ):
        """
        初始化同步问题

        Args:
            issue_type: 问题类型（delay, advance, missing等）
            severity: 严重程度（critical, major, minor）
            timestamp: 时间戳（秒）
            message: 问题描述
            details: 详细信息
        """
        self.issue_type = issue_type
        self.severity = severity
        self.timestamp = timestamp
        self.message = message
        self.details = details or {}

    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "type": self.issue_type,
            "severity": self.severity,
            "timestamp": self.timestamp,
            "message": self.message,
            "details": self.details,
        }

    def __str__(self) -> str:
        return f"[{self.severity}] {self.issue_type} @ {self.timestamp:.2f}s: {self.message}"

    def __repr__(self) -> str:
        return f"SyncIssue(type={self.issue_type}, severity={self.severity}, timestamp={self.timestamp})"


class SyncReport:
    """同步报告"""

    def __init__(self):
        """初始化同步报告"""
        self.issues: List[SyncIssue] = []
        self.video_duration = 0.0
        self.audio_duration = 0.0
        self.subtitle_duration = 0.0
        self.overall_sync_score = 0.0  # 0-100，100为完美同步
        self.summary: Dict[str, Any] = {}

    def add_issue(self, issue: SyncIssue):
        """
        添加问题

        Args:
            issue: 同步问题
        """
        self.issues.append(issue)

    def calculate_score(self):
        """计算同步分数"""
        if not self.issues:
            self.overall_sync_score = 100.0
            return

        # 根据问题严重程度扣分
        critical_penalty = 10.0
        major_penalty = 5.0
        minor_penalty = 1.0

        total_penalty = 0.0
        for issue in self.issues:
            if issue.severity == "critical":
                total_penalty += critical_penalty
            elif issue.severity == "major":
                total_penalty += major_penalty
            else:  # minor
                total_penalty += minor_penalty

        # 基础分数减去惩罚
        self.overall_sync_score = max(0.0, 100.0 - total_penalty)

    def generate_summary(self):
        """生成摘要"""
        self.calculate_score()

        # 统计问题
        issue_counts = {}
        for issue in self.issues:
            issue_counts[issue.issue_type] = issue_counts.get(issue.issue_type, 0) + 1

        # 按严重程度分类
        severity_counts = {"critical": 0, "major": 0, "minor": 0}
        for issue in self.issues:
            severity_counts[issue.severity] += 1

        self.summary = {
            "total_issues": len(self.issues),
            "sync_score": round(self.overall_sync_score, 2),
            "issue_types": issue_counts,
            "severity_distribution": severity_counts,
            "durations": {
                "video": self.video_duration,
                "audio": self.audio_duration,
                "subtitle": self.subtitle_duration,
            },
        }

    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "summary": self.summary,
            "issues": [issue.to_dict() for issue in self.issues],
            "durations": {
                "video": self.video_duration,
                "audio": self.audio_duration,
                "subtitle": self.subtitle_duration,
            },
        }

    def save_report(self, output_path: Path):
        """
        保存报告

        Args:
            output_path: 输出路径
        """
        output_path.parent.mkdir(parents=True, exist_ok=True)

        with open(output_path, "w", encoding="utf-8") as f:
            json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)

        logger.info(f"Sync report saved to: {output_path}")


class SyncValidator:
    """同步验证器"""

    def __init__(self, tolerance_ms: int = 100):
        """
        初始化验证器

        Args:
            tolerance_ms: 容差（毫秒）
        """
        self.tolerance_ms = tolerance_ms
        self.tolerance_sec = tolerance_ms / 1000.0

    def validate_sync(
        self,
        video_file: Path,
        audio_file: Optional[Path] = None,
        subtitle_file: Optional[Path] = None,
    ) -> SyncReport:
        """
        验证同步

        Args:
            video_file: 视频文件
            audio_file: 音频文件（可选）
            subtitle_file: 字幕文件（可选）

        Returns:
            同步报告
        """
        report = SyncReport()

        # 获取视频信息
        self._get_video_info(video_file, report)

        # 获取音频信息
        if audio_file:
            self._get_audio_info(audio_file, report)

        # 获取字幕信息
        if subtitle_file:
            self._get_subtitle_info(subtitle_file, report)

        # 检查视频音频同步
        if audio_file:
            self._check_video_audio_sync(video_file, audio_file, report)

        # 检查字幕同步
        if subtitle_file:
            self._check_subtitle_sync(video_file, subtitle_file, report)

        # 生成摘要
        report.generate_summary()

        return report

    def _get_video_info(self, video_file: Path, report: SyncReport):
        """
        获取视频信息

        Args:
            video_file: 视频文件
            report: 报告
        """
        try:
            result = subprocess.run(
                ["ffprobe", "-v", "error", "-show_entries",
                 "format=duration", "-of", "csv=p=0", str(video_file)],
                capture_output=True,
                text=True,
                timeout=10,
            )

            if result.returncode == 0:
                report.video_duration = float(result.stdout.strip())
        except Exception as e:
            logger.warning(f"Failed to get video info: {e}")

    def _get_audio_info(self, audio_file: Path, report: SyncReport):
        """
        获取音频信息

        Args:
            audio_file: 音频文件
            report: 报告
        """
        try:
            result = subprocess.run(
                ["ffprobe", "-v", "error", "-show_entries",
                 "format=duration", "-of", "csv=p=0", str(audio_file)],
                capture_output=True,
                text=True,
                timeout=10,
            )

            if result.returncode == 0:
                report.audio_duration = float(result.stdout.strip())
        except Exception as e:
            logger.warning(f"Failed to get audio info: {e}")

    def _get_subtitle_info(self, subtitle_file: Path, report: SyncReport):
        """
        获取字幕信息

        Args:
            subtitle_file: 字幕文件
            report: 报告
        """
        try:
            segments = SubtitleParser.load_subtitles(subtitle_file)
            if segments:
                report.subtitle_duration = segments[-1].end_time
        except Exception as e:
            logger.warning(f"Failed to get subtitle info: {e}")

    def _check_video_audio_sync(
        self,
        video_file: Path,
        audio_file: Path,
        report: SyncReport,
    ):
        """
        检查视频音频同步

        Args:
            video_file: 视频文件
            audio_file: 音频文件
            report: 报告
        """
        # 比较时长
        duration_diff = abs(report.video_duration - report.audio_duration)

        if duration_diff > self.tolerance_sec:
            issue = SyncIssue(
                issue_type="duration_mismatch",
                severity="major" if duration_diff > 1.0 else "minor",
                timestamp=0.0,
                message=f"Video and audio duration differ by {duration_diff:.2f}s",
                details={
                    "video_duration": report.video_duration,
                    "audio_duration": report.audio_duration,
                    "difference": duration_diff,
                },
            )
            report.add_issue(issue)

        # 检查音轨
        self._check_audio_track(video_file, audio_file, report)

    def _check_audio_track(
        self,
        video_file: Path,
        audio_file: Path,
        report: SyncReport,
    ):
        """
        检查音轨

        Args:
            video_file: 视频文件
            audio_file: 音频文件
            report: 报告
        """
        # 这里可以添加更复杂的音轨分析
        # 例如使用音频指纹技术

    def _check_subtitle_sync(
        self,
        video_file: Path,
        subtitle_file: Path,
        report: SyncReport,
    ):
        """
        检查字幕同步

        Args:
            video_file: 视频文件
            subtitle_file: 字幕文件
            report: 报告
        """
        try:
            segments = SubtitleParser.load_subtitles(subtitle_file)

            if not segments:
                return

            # 检查每个字幕段
            for segment in segments:
                # 检查是否在视频范围内
                if segment.start_time < -self.tolerance_sec:
                    issue = SyncIssue(
                        issue_type="subtitle_early",
                        severity="minor",
                        timestamp=segment.start_time,
                        message=f"Subtitle starts before video: {segment.text[:50]}",
                        details={
                            "start_time": segment.start_time,
                            "text": segment.text,
                        },
                    )
                    report.add_issue(issue)

                if segment.end_time > report.video_duration + self.tolerance_sec:
                    issue = SyncIssue(
                        issue_type="subtitle_late",
                        severity="minor",
                        timestamp=segment.start_time,
                        message=f"Subtitle extends beyond video: {segment.text[:50]}",
                        details={
                            "end_time": segment.end_time,
                            "video_duration": report.video_duration,
                            "text": segment.text,
                        },
                    )
                    report.add_issue(issue)

                # 检查字幕时长
                duration = segment.end_time - segment.start_time
                if duration > 10.0:  # 超过10秒
                    issue = SyncIssue(
                        issue_type="subtitle_too_long",
                        severity="major",
                        timestamp=segment.start_time,
                        message=f"Subtitle too long ({duration:.2f}s): {segment.text[:50]}",
                        details={
                            "duration": duration,
                            "text": segment.text,
                        },
                    )
                    report.add_issue(issue)

        except Exception as e:
            logger.error(f"Failed to check subtitle sync: {e}")


class AudioVideoAnalyzer:
    """音视频分析器"""

    def __init__(self):
        """初始化分析器"""
        self.has_ffmpeg = self._check_ffmpeg()

    def _check_ffmpeg(self) -> bool:
        """检查FFmpeg是否可用"""
        try:
            result = subprocess.run(
                ["ffmpeg", "-version"],
                capture_output=True,
                text=True,
                timeout=5,
            )
            return result.returncode == 0
        except (subprocess.TimeoutExpired, FileNotFoundError):
            return False

    def extract_audio(self, video_file: Path, output_path: Path) -> bool:
        """
        从视频提取音频

        Args:
            video_file: 视频文件
            output_path: 输出路径

        Returns:
            是否成功
        """
        if not self.has_ffmpeg:
            logger.error("FFmpeg not available")
            return False

        try:
            result = subprocess.run(
                ["ffmpeg", "-i", str(video_file), "-vn", "-acodec", "copy",
                 str(output_path), "-y"],
                capture_output=True,
                text=True,
                timeout=300,
            )
            return result.returncode == 0
        except Exception as e:
            logger.error(f"Failed to extract audio: {e}")
            return False

    def get_audio_peak(self, audio_file: Path) -> Optional[float]:
        """
        获取音频峰值

        Args:
            audio_file: 音频文件

        Returns:
            峰值（dB）
        """
        if not self.has_ffmpeg:
            return None

        try:
            # 使用ffmpeg的 silencedetect滤镜
            result = subprocess.run(
                ["ffmpeg", "-i", str(audio_file), "-af", "silencedetect",
                 "-f", "null", "-"],
                capture_output=True,
                text=True,
                timeout=60,
            )

            # 解析输出
            for line in result.stderr.split("\n"):
                if "silence_end" in line:
                    # 提取静音结束时间
                    import re
                    match = re.search(r"silence_end: ([\d.]+)", line)
                    if match:
                        return float(match.group(1))

            return None
        except Exception as e:
            logger.error(f"Failed to get audio peak: {e}")
            return None

    def detect_audio_gaps(self, audio_file: Path, threshold: float = -40.0) -> List[Dict[str, float]]:
        """
        检测音频间隙

        Args:
            audio_file: 音频文件
            threshold: 静音阈值（dB）

        Returns:
            间隙列表
        """
        if not self.has_ffmpeg:
            return []

        gaps = []

        try:
            result = subprocess.run(
                ["ffmpeg", "-i", str(audio_file), "-af",
                 f"silencedetect=noise={threshold}dB:duration=0.5",
                 "-f", "null", "-"],
                capture_output=True,
                text=True,
                timeout=60,
            )

            # 解析静音段
            for line in result.stderr.split("\n"):
                if "silence_start" in line:
                    import re
                    match = re.search(r"silence_start: ([\d.]+)", line)
                    if match:
                        gaps.append({
                            "start": float(match.group(1)),
                            "end": None,
                            "type": "silence",
                        })

            return gaps
        except Exception as e:
            logger.error(f"Failed to detect audio gaps: {e}")
            return []

    def analyze_synchronization(self, video_file: Path) -> Dict[str, Any]:
        """
        分析视频同步

        Args:
            video_file: 视频文件

        Returns:
            分析结果
        """
        if not self.has_ffmpeg:
            return {"error": "FFmpeg not available"}

        analysis = {
            "has_video": False,
            "has_audio": False,
            "video_codec": None,
            "audio_codec": None,
            "video_duration": 0.0,
            "audio_duration": 0.0,
            "frame_rate": 0.0,
            "resolution": None,
        }

        try:
            result = subprocess.run(
                ["ffprobe", "-v", "error", "-print_format", "json",
                 "-show_streams", str(video_file)],
                capture_output=True,
                text=True,
                timeout=10,
            )

            if result.returncode == 0:
                data = json.loads(result.stdout)
                for stream in data.get("streams", []):
                    if stream.get("codec_type") == "video":
                        analysis["has_video"] = True
                        analysis["video_codec"] = stream.get("codec_name")
                        analysis["frame_rate"] = eval(stream.get("r_frame_rate", "0/1"))
                        analysis["resolution"] = (
                            stream.get("width"),
                            stream.get("height")
                        )
                    elif stream.get("codec_type") == "audio":
                        analysis["has_audio"] = True
                        analysis["audio_codec"] = stream.get("codec_name")

                # 获取时长
                format_result = subprocess.run(
                    ["ffprobe", "-v", "error", "-show_entries",
                     "format=duration", "-of", "csv=p=0", str(video_file)],
                    capture_output=True,
                    text=True,
                    timeout=10,
                )

                if format_result.returncode == 0:
                    analysis["video_duration"] = float(format_result.stdout.strip())
                    analysis["audio_duration"] = analysis["video_duration"]

        except Exception as e:
            logger.error(f"Failed to analyze video: {e}")
            analysis["error"] = str(e)

        return analysis


class SyncQualityGrader:
    """同步质量分级器"""

    GRADE_THRESHOLDS = {
        "A": 95,  # 优秀
        "B": 85,  # 良好
        "C": 70,  # 一般
        "D": 50,  # 较差
        "F": 0,   # 不及格
    }

    @staticmethod
    def grade_sync_quality(sync_report: SyncReport) -> Tuple[str, str]:
        """
        分级同步质量

        Args:
            sync_report: 同步报告

        Returns:
            (等级, 描述)
        """
        score = sync_report.overall_sync_score

        if score >= SyncQualityGrader.GRADE_THRESHOLDS["A"]:
            return "A", "Excellent - Perfect synchronization"
        elif score >= SyncQualityGrader.GRADE_THRESHOLDS["B"]:
            return "B", "Good - Minor sync issues"
        elif score >= SyncQualityGrader.GRADE_THRESHOLDS["C"]:
            return "C", "Fair - Some sync issues present"
        elif score >= SyncQualityGrader.GRADE_THRESHOLDS["D"]:
            return "D", "Poor - Significant sync issues"
        else:
            return "F", "Fail - Severe synchronization problems"

    @staticmethod
    def get_recommendations(sync_report: SyncReport) -> List[str]:
        """
        获取改进建议

        Args:
            sync_report: 同步报告

        Returns:
            建议列表
        """
        recommendations = []

        # 根据分数给出建议
        if sync_report.overall_sync_score < 50:
            recommendations.append("Critical sync issues detected. Please review your timing configuration.")
        elif sync_report.overall_sync_score < 70:
            recommendations.append("Some sync issues detected. Consider adjusting your subtitle timing.")

        # 根据问题类型给出建议
        issue_types = set(issue.issue_type for issue in sync_report.issues)

        if "duration_mismatch" in issue_types:
            recommendations.append("Video and audio durations don't match. Check your export settings.")

        if "subtitle_early" in issue_types or "subtitle_late" in issue_types:
            recommendations.append("Subtitle timing is off. Verify your subtitle synchronization.")

        if "subtitle_too_long" in issue_types:
            recommendations.append("Some subtitles are too long. Consider breaking them up.")

        if not recommendations:
            recommendations.append("Good synchronization! No major issues detected.")

        return recommendations
